//@author A0088310L
#include "StdAfx.h"
#include "Executor.h"
#ifdef LOG1
#include"log1.h"
#else
#include "Log.h"

#endif

//@author A0088310L
enum Executor::COMMAND_TYPE;
enum Executor::KEYWORD_TYPE;
enum Executor::SHOW_TYPE;
vector<Task*> Executor::taskList;
string Executor::display = EMPTY_STRING;

void Executor::loadVect()
{
	Storage store;

	taskList = store.loadVect();
	saveCommand(INITIAL);
}

void Executor::saveCommand(string command)
{
	Storage store;

	store.saveCommand(command);
}

void Executor::storeToFile()
{
	Storage store;

	store.storeResult(taskList);
}      

Executor::COMMAND_TYPE Executor::determineCommandType(string commandTypeString_lowered)
{
        if(commandTypeString_lowered == "add") {
			return COMMAND_TYPE::ADD;
        } else if (commandTypeString_lowered == "delete") {
            return COMMAND_TYPE::DELET;
        } else if (commandTypeString_lowered == "show") {
            return COMMAND_TYPE::SHOW;
        } else if (commandTypeString_lowered == "search") {
            return COMMAND_TYPE::SEARCH;
        } else if (commandTypeString_lowered == "clear") {
            return COMMAND_TYPE::CLEAR;
        } else if (commandTypeString_lowered == "done") {
            return COMMAND_TYPE::DONE;
		} else if (commandTypeString_lowered == "undone") {
            return COMMAND_TYPE::UNDONE;
		} else if (commandTypeString_lowered == "edit") {
            return COMMAND_TYPE::EDIT;
		} else if (commandTypeString_lowered == "postpone") {
            return COMMAND_TYPE::POSTPONE;
		} else if (commandTypeString_lowered == "undo") {
            return COMMAND_TYPE::UNDO;
        } else if (commandTypeString_lowered == "help") {
            return COMMAND_TYPE::HELP;
        } else if (commandTypeString_lowered == "exit") {
            return COMMAND_TYPE::EXIT;
        } else {
			return COMMAND_TYPE::INVALID;
        }
}

Executor::KEYWORD_TYPE Executor::determineKeywordType(string keywordTypeString)
{
	if(keywordTypeString == "-name")
		return KEYWORD_TYPE::NAME;
    else if(keywordTypeString == "-date")
        return KEYWORD_TYPE::DATE;
    else if(keywordTypeString == "-starts")
        return KEYWORD_TYPE::STARTS;
    else if(keywordTypeString == "-ends")
        return KEYWORD_TYPE::ENDS;
    else if(keywordTypeString == "-impt")
        return KEYWORD_TYPE::IMPT;
	else if(keywordTypeString == "-unimpt")
        return KEYWORD_TYPE::UNIMPT;
	else if(keywordTypeString == "-repeat")
        return KEYWORD_TYPE::REPEAT;
	else //(keywordTypeString == "-detail")
        return KEYWORD_TYPE::DETAIL;
}

Executor::SHOW_TYPE Executor::determineShowType(string showTypeString_lowered)
{
	if(showTypeString_lowered == "impt")
        return SHOW_TYPE::SHOW_IMPT;
	else if(showTypeString_lowered == "done")
        return SHOW_TYPE::SHOW_DONE;
    else if(showTypeString_lowered == "undone")
        return SHOW_TYPE::SHOW_UNDONE;
    else if(showTypeString_lowered == "detail")
        return SHOW_TYPE::SHOW_DETAIL;
    else if(showTypeString_lowered == "time")
        return SHOW_TYPE::SHOW_TIME;
	else if(showTypeString_lowered == "floating")
        return SHOW_TYPE::SHOW_FLOATING;
	else if(showTypeString_lowered == "")
        return SHOW_TYPE::SHOW_ALL;
	else 
		return SHOW_TYPE::SHOW_DATE;
}

string Executor::toLowerCase(string word)
{
    for(int i=0; i< word.size(); i++){
        word[i]=tolower(word[i]);
    }

    return word;
}

string Executor::getFirstWord(string inputLine) 
{
    string firstWord = inputLine.substr(0, inputLine.find(' '));
    return firstWord;
}

string Executor::extractFirstWord(string &inputLine)
{
    string firstWord = "";
    int i = inputLine.find(' ');
    firstWord = inputLine.substr(0, i);
    if(i != -1)
		inputLine.erase(0, i+1);
    else
        inputLine.clear();
        
    return firstWord;
}

string Executor::executeCommand(string inputLine)
{
    Validator checker;
	UserInterface status;
    Storage store;
	string curr_display;
	
	string commandTypeString  = extractFirstWord(inputLine);
	try
	{
		string commandTypeString_lowered = toLowerCase(commandTypeString);

		if(!checker.isValidCommand(commandTypeString_lowered))
			throw Exception("invalidCommand");
		
		COMMAND_TYPE commandType = determineCommandType(commandTypeString_lowered);
		switch (commandType) {
			case ADD:
				curr_display = executeAdd(inputLine);
				break;
			case DELET: 
				curr_display = executeDelete(inputLine);
				break;
			case SHOW: 
				curr_display = executeShow(inputLine);
				break;
			case SEARCH: 
				curr_display = executeSearch(inputLine);
				break;
			case CLEAR: 
				curr_display = executeClear(inputLine);
				break;
			case DONE: 
				curr_display = executeDone(inputLine);
				break;
			case UNDONE: 
				curr_display = executeUndone(inputLine);
				break;
			case EDIT: 
				curr_display = executeEdit(inputLine);
				break;
			case POSTPONE: 
				curr_display = executePostpone(inputLine);
				break;
			case UNDO: 
				curr_display = executeUndo();
				break;
			case HELP: 
				curr_display = executeHelp();
				break;
			case EXIT: 
				curr_display = executeExit();
				break;
			default:
				curr_display = ERR;
				break;
		}
		if(curr_display != EMPTY_STRING)	// display has changed. means command has been executed.
		{
			display = curr_display;
			if(!(commandType == SHOW || commandType == SEARCH || commandType == UNDO || commandType == HELP || commandType == EXIT))
			{
				saveCommand(commandTypeString_lowered);
				storeToFile();
			}
		}
		FILE* pFile = fopen("application.log", "a");
		Output2FILE::Stream() = pFile;
		FILE_LOG(logINFO) <<endl<< "EXECUTE:"<<endl<<'\t'<<commandTypeString<<" "<<inputLine<<endl<<"RESULT:"<<endl<<curr_display;
		
	}
	catch (Exception& e)
	{
		status.generateMessage(e.getErrorType(), commandTypeString);
		FILE* pFile = fopen("application.log", "a");
		Output2FILE::Stream() = pFile;
		FILE_LOG(logERROR) << e.getErrorType();
	}

	return display;
}

string Executor::executeAdd(string inputLine) 
{
    Task *t;
	UserInterface status;
    Storage store;
    string result, display = EMPTY_STRING;

	int prev_taskListSize = taskList.size()-1;

    t = new Task();
    result = makeTask(inputLine, t);

    if(result == FORMATTED)
	{
		addToTaskList(t);
		int curr_taskListSize = taskList.size()-1;
		string newTasks = getNewTasksInfo(prev_taskListSize, curr_taskListSize);
		status.generateMessage("added", t->getName());
        display = newTasks + TAB + "has been added";
	}

    return display;
}

string Executor::getNewTasksInfo(int prev_taskListSize, int curr_taskListSize)
{
	string newTasks = EMPTY_STRING;

	ostringstream outstr;
	
	for(int i = prev_taskListSize+1; i<= curr_taskListSize; i++)
		outstr << basicTaskInfo(i) << "\n";

    newTasks = outstr.str();

	return newTasks;
}

void Executor::addToTaskList(Task* t)
{
    UserInterface status;
	string display_string;

	taskList.push_back(t);
}

string Executor::makeTask(string inputLine, Task* &t)
{
	UserInterface status;
    Validator checker;
    string result = FORMATTED;
	string result_forTiming;

    result = makeTaskName(inputLine, t);

	while(result == FORMATTED  && !inputLine.empty())
	{
		string keyWord = extractFirstWord(inputLine);
		KEYWORD_TYPE keywordType = determineKeywordType(keyWord);

		switch(keywordType) 
		{
		case DATE:
			result = makeTaskDate(inputLine, t);
			break;
		case STARTS:
			result = makeTaskStartTime(inputLine, t);
			break;
		case ENDS:
			result = makeTaskEndTime(inputLine, t);
			break;
		case DETAIL:
			result = makeTaskDetail(inputLine, t);
			break;
		case IMPT:
			result = makeTaskImpt(t);
			break;
		case REPEAT:
			result = makeTaskRepeat(inputLine, t);
			break;
		}
	}
	
	if(t->getDate() != "")
		result_forTiming = makeProperTimings(t);
	if(result == FORMATTED && result_forTiming == FORMATTED)
		result = FORMATTED;

	return result;
}

string Executor::makeString(int time)
{
	string resultString;          

	ostringstream convert;  
	convert << time;  

	resultString = convert.str();

	return resultString;
}

string Executor::makeProperTimings(Task* &t)
{
	Validator checker;
	UserInterface status;
	int startTime, endTime;
	string startTime_str, endTime_str, endTimeSet_str, startTimeSet_str, result;

	startTime = t->getStartTime();
	endTime = t->getEndTime();

	startTime_str = makeString(startTime);
	endTime_str = makeString(endTime);

	string date = t->getDate();

	// if no st time : 0000-1600 becomes 1500-1600
	if(startTime == 0 && endTime != 0)
	{
		try{
		startTime = endTime - ONE_HOUR;
		startTime_str = makeString(startTime);
		if(!checker.isValidTimeRange(startTime_str))
			throw Exception("invalidTime");
		if(!checker.isValidTimeDeadline(startTime_str, date))
			throw Exception("invalidTime");
		t->setStartTime(startTime);
		result = FORMATTED;
		}
		catch(Exception &e)
		{
			if(!checker.isValidTimeRange(startTime_str))
				status.generateMessage(e.getErrorType(), startTime_str, "invalid as it is out of range of ");
			else //validity compared to task deadline
				status.generateMessage(e.getErrorType(), endTime_str, "invalid as start time would be past current");
		}
	}
	// if no end time : 1600-0000 becomes 1600-1700
	else if(startTime != 0 && endTime == 0)
	{
		try {
		endTime = startTime + ONE_HOUR;
		endTime_str = makeString(endTime);
		if(!checker.isValidTimeRange(endTime_str))
			throw Exception("invalidTime");
		if(!checker.isValidTimeDeadline(endTime_str, date))
			throw Exception("invalidTime");
		t->setEndTime(endTime);
		result = FORMATTED;
		}
		catch(Exception &e)
		{
			if(!checker.isValidTimeRange(endTime_str))
				status.generateMessage(e.getErrorType(), endTime_str, "invalid as it is out of range of ");
			else //validity compared to task deadline
				status.generateMessage(e.getErrorType(), startTime_str, "invalid as end time would be past deadline's ");
		}
	}
	// if no st and end time : 0000-0000 
	// both st and end time are filled with valid values
	else
		result = FORMATTED;

	return result;
}

string Executor::makeTaskName(string &inputLine, Task* &t)
{
	Validator checker;
	UserInterface status;
	string followingWord, name;
	string result;

	followingWord = getFirstWord(inputLine);
	while(!checker.isValidKeyword(followingWord) && !inputLine.empty())
	{
		string namePart = extractFirstWord(inputLine);
        if(name == "")
			name = namePart;
		else
			name = name+" "+namePart;
		followingWord = getFirstWord(inputLine);
	}
	
	try
	{
		if(!checker.isValidTaskName(name))
			throw Exception("invalidName");
		t->appendToName(name);
		result = FORMATTED;
	}
	catch (Exception& e)
 	{						
		status.generateMessage(e.getErrorType(), name);				//'display' does not change for wrong input, only status updated
	}
	
	return result;
}

string Executor::makeTaskDate(string &inputLine, Task* &t)
{
	Validator checker;
	string result;
	UserInterface status;

	string date = extractFirstWord(inputLine);
	try 
	{
		if(!checker.isValidDate(date)) 
			throw Exception("invalidDate");
		t->setDate(date);
		result = FORMATTED;
	}
	catch (Exception& e)
	{
		status.generateMessage(e.getErrorType(), date);
	}

	return result;
}

string Executor::makeTaskStartTime(string &inputLine, Task* &t)
{
	Validator checker;
	UserInterface status;
	string result;

    string start_time = extractFirstWord(inputLine);
	string end_time = makeString(t->getEndTime());
	string date = t->getDate();
	try
	{
		if(t->getDate() == "")
			date = checker.getCurrentDate();
		if(!checker.isInt(start_time))
            throw Exception("invalidTime");
		if(!checker.isValidTimeRange(start_time))
			throw Exception("invalidTime");
		if(!checker.isValidTimeDeadline(start_time, date))
			throw Exception("invalidTime");
		if((makeInt(end_time) != 0) &&  (makeInt(end_time)<= makeInt(start_time)))
			throw Exception("invalidTime");
		t->setDate(date);
		t->setStartTime(makeInt(start_time));
		result = FORMATTED;
	}
	catch (Exception& e)
    {
		if(!checker.isInt(start_time))
			status.generateMessage(e.getErrorType(), start_time, "not an integer");
		else if(!checker.isValidTimeRange(start_time))
			status.generateMessage(e.getErrorType(), start_time, "invalid as it is out of range of ");
		else if(!checker.isValidTimeDeadline(start_time, date))
			status.generateMessage(e.getErrorType(), start_time, "invalid as current time has passed this");
		else
			status.generateMessage(e.getErrorType(), start_time, "same as end ");
	}
    
	return result;
}

string Executor::makeTaskEndTime(string &inputLine, Task* &t)
{
	Validator checker;
	UserInterface status;
	string result;

    string end_time = extractFirstWord(inputLine);
	string start_time = makeString(t->getStartTime());
	string date = t->getDate();
    try
	{
		if(t->getDate() == "")
			date = checker.getCurrentDate();
		if(!checker.isInt(end_time))
            throw Exception("invalidTime");
		if(!checker.isValidTimeRange(end_time))
			throw Exception("invalidTime");
		if(!checker.isValidTimeDeadline(end_time, date))
			throw Exception("invalidTime");
		if((makeInt(end_time) != 0) &&  (makeInt(end_time)<= makeInt(start_time)))
			throw Exception("invalidTime");
		t->setDate(date);
		t->setEndTime(makeInt(end_time));
		result = FORMATTED;
    }
	catch (Exception& e)
    {
		if(!checker.isInt(end_time))
			status.generateMessage(e.getErrorType(), end_time, "not an integer");
		else if(!checker.isValidTimeRange(end_time))
			status.generateMessage(e.getErrorType(), end_time, "invalid as it is out of range of");
		else if(!checker.isValidTimeDeadline(end_time, date))
			status.generateMessage(e.getErrorType(), end_time, "invalid as current time has passed this");
		else
			status.generateMessage(e.getErrorType(), end_time, "same as start ");
	}
    
	return result;
}

string Executor::makeTaskImpt(Task* &t)
{
	string result;
	
	t->setImpt(true);
	result = FORMATTED;

	return result;
}

string Executor::makeTaskDetail(string &inputLine, Task* &t)
{
	Validator checker;
	UserInterface status;
	string followingWord;
	string result = FORMATTED;

	followingWord = getFirstWord(inputLine);
	while(!checker.isValidKeyword(followingWord) && !inputLine.empty())
	{
		string detail = extractFirstWord(inputLine);
        t->appendToDetail(detail);
        followingWord = getFirstWord(inputLine);
    }

	return result;
}

string Executor::makeTaskRepeat(string &inputLine, Task* t)
{
	Validator checker;
	string result;
	UserInterface status;
	Task *t_temp;
	
	vector<string> datesToRepeat;
	string frequency = extractFirstWord(inputLine);
	string date = t->getDate();
	string noFurther, noFurtherIn, noFurtherDateOn_specific;

	try 
	{
		if(date == "")
			throw Exception("undatedTaskToRepeat");
		if(!checker.isValidFrequency(frequency))
			throw Exception("invalidFreqToRepeat");
		datesToRepeat = checker.repeatMode(date, frequency);
		if(datesToRepeat.empty())
		{
			noFurther = checker.noFurther(frequency);
			noFurtherIn = checker.noFurtherIn(frequency);
			noFurtherDateOn_specific = checker.whenNoMoreDate(date, frequency);
			throw Exception("noDateToRepeat");
		}
		for(int i=0; i<datesToRepeat.size(); i++)
		{
			t_temp = new Task();
			copyTask(t_temp, t);
			t_temp->setDate(datesToRepeat[i]);
			addToTaskList(t_temp);
		}
		result = FORMATTED;
	}
	catch (Exception& e)
	{
		if(date == "")
			status.generateMessage(e.getErrorType(), t->getName());
		else if(datesToRepeat.empty())
			status.generateMessage(e.getErrorType(), noFurther, noFurtherIn, noFurtherDateOn_specific);
		else
			status.generateMessage(e.getErrorType(), frequency);
	}

	return result;
}

void Executor::copyTask(Task* &t_temp, Task* t)
{
	string taskName = t->getName();
	int taskStart = t->getStartTime();
	int taskEnd = t->getEndTime();
	bool impt = t->getImpt();
	bool done = t->getDone();
	string detail = t->getDetail();

	t_temp->appendToName(taskName);
	t_temp->setStartTime(taskStart);
	t_temp->setEndTime(taskEnd);
	t_temp->setImpt(impt);
	t_temp->setDone(done);
	t_temp->appendToDetail(detail);	
}

string Executor::basicTaskInfo(int taskIndex)
{
	string basicTaskInfo = EMPTY_STRING;
	ostringstream outstr;
	Task *t;

	assert(taskIndex>=0 && taskIndex<taskList.size());

	t = taskList[taskIndex];
	
	outstr << taskIndex+1 << ". " << TAB << "Name : " << t->getName() << "\n";
	if(t->getDate() != "")
		outstr << TAB << "Date : " << t->getDate() << "\n";
	if(t->getStartTime() != 0 && t->getEndTime() != 0)
	{
		outstr << TAB << "Start Time : " << t->getStartTime() << "\n";
		outstr << TAB << "End Time : " << t->getEndTime() << "\n";
	}
	if(t->getDone() == true)
		outstr << TAB << "Task Status : Completed" << "\n";
	else
		outstr << TAB << "Task Status : Incomplete" << "\n";

    basicTaskInfo = outstr.str();

	return basicTaskInfo;
}

string Executor::allTaskInfo(int taskIndex)
{
	string allTaskInfo = EMPTY_STRING;
	ostringstream outstr;
	Task *t;

	assert(taskIndex>=0 && taskIndex<taskList.size());

	t = taskList[taskIndex];
	
	outstr << taskIndex+1 << ". " << TAB << "Name : " << t->getName() << "\n";
	if(t->getDate() != "")
		outstr << TAB << "Date : " << t->getDate() << "\n";
	if(t->getStartTime() != 0 && t->getEndTime() != 0)
	{
		outstr << TAB << "Start Time : " << t->getStartTime() << "\n";
		outstr << TAB << "End Time : " << t->getEndTime() << "\n";
	}
	if(t->getDetail() != "")
        outstr << TAB << "Detail : " << t->getDetail() << "\n";
	if(t->getImpt() == true)
		outstr << TAB << "Task Priority : Important" << "\n";
	else
		outstr << TAB << "Task Priority : Normal" << "\n";
	if(t->getDone() == true)
		outstr << TAB << "Task Status : Completed" << "\n";
	else
		outstr << TAB << "Task Status : Incomplete" << "\n";
	
	allTaskInfo = outstr.str();

	return allTaskInfo;
}

string Executor::executeDelete(string inputLine)
{
    UserInterface status;
    Storage store;
    Validator checker;
    string display;
	
	string taskNum_str = extractFirstWord(inputLine);
	try {
		if(!checker.isInt(taskNum_str)) 
			throw Exception("notInt");
		int taskNum_int = makeInt(taskNum_str);
		int actual_taskNum_int = taskNum_int - 1;
		if(!checker.isValidTaskNumber(actual_taskNum_int, taskList))
			throw Exception("invalidTaskNum");
		string deletedbasicTaskInfo = basicTaskInfo(actual_taskNum_int);
		string deletedTaskName = taskList[actual_taskNum_int]->getName();
		taskList.erase(taskList.begin()+actual_taskNum_int);
		
		status.generateMessage("deleted", deletedTaskName);			//update 'status'
		display = deletedbasicTaskInfo + "\n" + TAB + "has been deleted";		//update 'display'
	}
	catch (Exception& e)
 	{
		status.generateMessage(e.getErrorType(), taskNum_str);
 	}
	
    return display;
}

int Executor::makeInt(string taskNum_str)
{
	assert(taskNum_str!=EMPTY_STRING);
    return atoi(taskNum_str.c_str());
}

string Executor::executeShow(string inputLine)
{
	UserInterface status;
	string display = EMPTY_STRING;

    string showTypeString = extractFirstWord(inputLine);
	try
	{
	if(taskList.size() == 0)
		throw Exception("isEmptyFile");
	string showTypeString_lowered = toLowerCase(showTypeString);
	SHOW_TYPE commandType = determineShowType(showTypeString_lowered);     
		switch (commandType) {
			case SHOW_IMPT:
				display = showImpt();
				break;
			case SHOW_DONE:
				display = showDone();
				break;
			case SHOW_UNDONE:
				display = showUndone();
				break;
			case SHOW_DETAIL:
				display = showDetail(inputLine);
				break;
			case SHOW_TIME:
				display = showTime(inputLine);
				break;
			case SHOW_FLOATING:
				display = showFloating();
				break;
			case SHOW_ALL:
				display = showAll();
				break;
			case SHOW_DATE:
				display = showDate(showTypeString);
				break;
			default:
				display = ERR;
				break;
		}
	}
	catch (Exception& e)
	{
		status.generateMessage(e.getErrorType(), "");
	}
    return display;
}

string Executor::showAll()
{
	string display = EMPTY_STRING;
	ostringstream outstr;

	for(int i = 0; i < taskList.size(); i++)
		outstr << basicTaskInfo(i) << "\n";

    display = outstr.str();

	return display;
}

string Executor::showImpt()
{
	UserInterface status;
	string display = EMPTY_STRING;
	ostringstream outstr;

	for(int i = 0; i < taskList.size(); i++)
	{
		bool tmp_impt = taskList[i]->getImpt();
		if(taskList[i]->getImpt() == true)
		{
			outstr << basicTaskInfo(i) << "\n";
		}
	}
	display = outstr.str();

	try
	{
		if(display == EMPTY_STRING)
			throw Exception("noTaskOfType");
	}
	catch(Exception& e)
	{
		status.generateMessage(e.getErrorType(), "important");	
	}

	return display;
}

string Executor::showDone()
{
	UserInterface status;
	string display = EMPTY_STRING;
	ostringstream outstr;

	for(int i = 0; i < taskList.size(); i++)
    {       
        if(taskList[i]->getDone() == true)
            outstr << basicTaskInfo(i) << "\n";
    }
	display = outstr.str();
	
	try
	{
		if(display == EMPTY_STRING)
			throw Exception("noTaskOfType");
	}
	catch(Exception& e)
	{
		status.generateMessage(e.getErrorType(), "done");
	}

	return display;
}

string Executor::showUndone()
{
	UserInterface status;
	string display = EMPTY_STRING;
	ostringstream outstr;

	for(int i = 0; i < taskList.size(); i++)
    {       
        if(taskList[i]->getDone() == false)
            outstr << basicTaskInfo(i) << "\n";
    }

	display = outstr.str();

	try
	{
		if(display == EMPTY_STRING)
			throw Exception("noTaskOfType");
	}
	catch(Exception& e)
	{
		status.generateMessage(e.getErrorType(), "undone");
	}

	return display;
}

string Executor::showDetail(string inputLine)
{
	Validator checker;
	UserInterface status;
	string display, taskName, detail = EMPTY_STRING;
	int actual_taskNum_int;
	ostringstream outstr;
    
	string taskNum_str = extractFirstWord(inputLine);
    try 
	{
		if(!checker.isInt(taskNum_str)) 
			throw Exception("notInt");
		int taskNum_int = makeInt(taskNum_str);
		actual_taskNum_int = taskNum_int - 1;
		if(!checker.isValidTaskNumber(actual_taskNum_int, taskList))
			throw Exception("invalidTaskNum");
        outstr << allTaskInfo(actual_taskNum_int);
                                
        display = outstr.str();
	}
	catch (Exception& e)
 	{
		status.generateMessage(e.getErrorType(), taskNum_str);
 	}

	return display;
}

string Executor::showTime(string inputLine)
{
	Validator checker;
	UserInterface status;
	string display, taskName = EMPTY_STRING;
	int actual_taskNum_int;
	ostringstream outstr;

	string taskNum_str = extractFirstWord(inputLine);
    try 
	{
		if(!checker.isInt(taskNum_str)) 
			throw Exception("notInt");
		int taskNum_int = makeInt(taskNum_str);
		actual_taskNum_int = taskNum_int - 1;
		if(!checker.isValidTaskNumber(actual_taskNum_int, taskList))
			throw Exception("invalidTaskNum");
		int startTime = taskList[actual_taskNum_int]->getStartTime();
		int endTime = taskList[actual_taskNum_int]->getEndTime();
		taskName = taskList[actual_taskNum_int]->getName();
		if(startTime != 0 || endTime != 0)
		{
			outstr << taskNum_int << "." << TAB <<  "Name : " << taskName << "\n";
			outstr << TAB << "Starts : " << startTime << "\n";
			outstr << TAB << "Ends : " << endTime  << "\n";                 
			display = outstr.str();
		}
		else
			throw Exception("noTimingTask");
	}
	catch (Exception& e)
 	{
		if(!checker.isInt(taskNum_str) || !checker.isValidTaskNumber(actual_taskNum_int, taskList))
			status.generateMessage(e.getErrorType(), taskNum_str);
		else
			status.generateMessage(e.getErrorType(), taskName);	
 	}

	return display;
}

string Executor::showFloating()
{
	UserInterface status;
	string display = EMPTY_STRING;
	ostringstream outstr;

	for(int i = 0; i < taskList.size(); i++)
    {       
        if(taskList[i]->getDate() == "")
			outstr << basicTaskInfo(i) << "\n";
    }

	display = outstr.str();

	try
	{
		if(display == EMPTY_STRING)
			throw Exception("noTaskOfType");
	}
	catch(Exception& e)
	{
		status.generateMessage(e.getErrorType(), "floating");	
	}

	return display;
}

string Executor::showDate(string date)
{
	Validator checker;
	UserInterface status;
	string display = EMPTY_STRING;
	ostringstream outstr;

    try
	{
		if(!checker.isValidDate(date))
		throw Exception("invalidDate");
    for(int i = 0; i < taskList.size(); i++)
    {       
        if(date == taskList[i]->getDate())
			outstr << basicTaskInfo(i) << "\n";
    }

	display = outstr.str();
	if(display == EMPTY_STRING)
		throw Exception("noTaskOnCmdDate");
	}
	catch(Exception& e)
	{
		status.generateMessage(e.getErrorType(), date, "shown");
	}

	return display;
}
        
string Executor::executeSearch(string inputLine)
{
	UserInterface status;
    size_t found;
	int match = 0;
    ostringstream outstr;
    string display = EMPTY_STRING; 

    string inputLine_lowCase = toLowerCase(inputLine);

    for(int i = 0; i < taskList.size(); i++)
    {
        string stringInVec = taskList[i]->getName();
        toLowerCase(stringInVec);
        found = stringInVec.find(inputLine);
        if(found!=string::npos)
        {
            match++;
			outstr << basicTaskInfo(i) << "\n";       
		}
    }

	display = outstr.str();

	try
	{
		if(display == EMPTY_STRING)
			throw Exception("matchUnfound");
	}
	catch(Exception& e)
	{
		status.generateMessage(e.getErrorType(), inputLine);
	}
 
    return display;
}

string Executor::executeClear(string inputLine)
{
    string display;
        
    string date = extractFirstWord(inputLine);

    if(date == "")
		display = clearAll();
	else
		display = clearTasksOnDate(date);
        
    return display;
}

string Executor::clearAll()
{
	UserInterface status;
	string display;

	taskList.clear();

	display = status.generateMessage("clearAll", ""); 

	return display;
}

string Executor::clearTasksOnDate(string date)
{
	UserInterface status;
    Validator checker;
	string display;
	int i = 0;

	try
	{
		if(!checker.isValidDate(date))
			throw Exception("invalidDate");
		for(int i = 0; i<taskList.size(); i++)
		{
			if(date == taskList[i]->getDate())
			{
				taskList.erase(taskList.begin()+i);
				display = status.generateMessage("cleared", date);
			}
		}
		if(display == EMPTY_STRING)
			throw Exception("noTaskOnCmdDate");
	}
	catch(Exception& e)
	{
		status.generateMessage(e.getErrorType(), date, "cleared");
	}

	return display;
}

string Executor::executeDone(string inputLine) {

	UserInterface status;
    Storage store;
    Validator checker;
	int actual_taskNum_int;
    string display, taskName;

    string taskNum_str = extractFirstWord(inputLine);
	try
	{
		if(!checker.isInt(taskNum_str)) 
			throw Exception("notInt");
		int taskNum_int = makeInt(taskNum_str);
		actual_taskNum_int = taskNum_int - 1;
		if(!checker.isValidTaskNumber(actual_taskNum_int, taskList))
			throw Exception("invalidTaskNum");
		string doneTask = basicTaskInfo(actual_taskNum_int);
		taskName = taskList[actual_taskNum_int]->getName();
		if(taskList[actual_taskNum_int]->getDone() == true)
			throw Exception("alreadyMarkedDone");
		taskList[actual_taskNum_int]->setDone(true);
		status.generateMessage("Done", taskName);
		display = doneTask + "\n"  + TAB + "has been marked as done";
	}
	catch (Exception& e)
 	{
		if(!checker.isInt(taskNum_str) || !checker.isValidTaskNumber(actual_taskNum_int, taskList))
			status.generateMessage(e.getErrorType(), taskNum_str);
		else
			status.generateMessage(e.getErrorType(), taskName);
 	}

    return display;
}

string Executor::executeUndone(string inputLine) {

	UserInterface status;
    Storage store;
    Validator checker;
	int actual_taskNum_int;
    string display, taskName;

    string taskNum_str = extractFirstWord(inputLine);
	try
	{
		if(!checker.isInt(taskNum_str)) 
			throw Exception("notInt");
		int taskNum_int = makeInt(taskNum_str);
		actual_taskNum_int = taskNum_int - 1;
		if(!checker.isValidTaskNumber(actual_taskNum_int, taskList))
			throw Exception("invalidTaskNum");
		string undoneTask = basicTaskInfo(actual_taskNum_int);
		taskName = taskList[actual_taskNum_int]->getName();
		if(taskList[actual_taskNum_int]->getDone() == false)
			throw Exception("alreadyMarkedUndone");
		taskList[actual_taskNum_int]->setDone(false);
		status.generateMessage("Undone", taskName);
		display = undoneTask + "\n"  + TAB + "has been marked as undone";
	}
	catch (Exception& e)
 	{
		if(!checker.isInt(taskNum_str) || !checker.isValidTaskNumber(actual_taskNum_int, taskList))
			status.generateMessage(e.getErrorType(), taskNum_str);
		else
			status.generateMessage(e.getErrorType(), taskName);
 	}

    return display;
}

string Executor::executeEdit(string inputLine)
{
	Validator checker;
	UserInterface status;
	int actual_taskNum_int;
	string firstWord, editInfo, taskName = EMPTY_STRING;
	string result, result_forTiming = EMPTY_STRING;

	string taskNum_str = extractFirstWord(inputLine);
	try
	{
		if(!checker.isInt(taskNum_str)) 
			throw Exception("notInt");
		int taskNum_int = makeInt(taskNum_str);
		actual_taskNum_int = taskNum_int - 1;
		if(!checker.isValidTaskNumber(actual_taskNum_int, taskList))
			throw Exception("invalidTaskNum");
		firstWord = extractFirstWord(inputLine);
		editInfo = inputLine;									//for later use in printing system messages
		taskName = taskList[actual_taskNum_int]->getName();		//for later use in printing system messages
		if(firstWord == "" || !checker.isValidKeyword(firstWord))
			throw Exception("invalidEdit");
		KEYWORD_TYPE keywordType = determineKeywordType(firstWord);
		switch(keywordType) 
		{
			case NAME:
				result = editName(inputLine, actual_taskNum_int);
				break;
			case DATE:
				result = makeTaskDate(inputLine, taskList[actual_taskNum_int]);
				break;
			case STARTS:
				result = makeTaskStartTime(inputLine, taskList[actual_taskNum_int]);
				break;
 			case ENDS:
				result = makeTaskEndTime(inputLine, taskList[actual_taskNum_int]);
				break;
			case IMPT:
				result = makeTaskImpt(taskList[actual_taskNum_int]);
				break;
			case UNIMPT:
				result = makeTaskNotImpt(taskList[actual_taskNum_int]);
				break;
			case DETAIL:
				result = editDetail(inputLine, actual_taskNum_int);
				break;
			case REPEAT:
				result = makeTaskRepeat(inputLine, taskList[actual_taskNum_int]);
				break;
			default:
				result = ERR;
				break;
		}
		if(taskList[actual_taskNum_int]->getDate() != "")
			result_forTiming = makeProperTimings(taskList[actual_taskNum_int]);
		if(result == FORMATTED && result_forTiming == FORMATTED)
		{
			status.generateMessage("editted", taskName, firstWord, editInfo);
			display = basicTaskInfo(actual_taskNum_int) + "\n" + TAB + "has been editted";
		}
	}
	catch (Exception& e)
 	{
		if(!checker.isInt(taskNum_str) || !checker.isValidTaskNumber(actual_taskNum_int, taskList))
			status.generateMessage(e.getErrorType(), taskNum_str);
		else //(firstWord == "") || (!checker.isValidKeyword(firstWord))
			status.generateMessage(e.getErrorType(), firstWord);
 	}

	return display;
}

string Executor::editName(string inputLine, int vectIndex)
{
	Validator checker;
	UserInterface status;
	string followingWord;
	string display;
	
	try
	{
		if(!checker.isValidTaskName(inputLine))
			throw Exception("invalidName");
		taskList[vectIndex]->clearName();
		display = makeTaskName(inputLine, taskList[vectIndex]);
	}
	catch(Exception& e)
	{
		status.generateMessage(e.getErrorType(), inputLine);
	}
	        
	return display;
}

string Executor::makeTaskNotImpt(Task* &t)
{
	string display = FORMATTED;
	
	t->setImpt(false);

	return display;
}

string Executor::editDetail(string inputLine, int vectIndex)
{	
	string display;

	taskList[vectIndex]->clearDetail();
	display = makeTaskDetail(inputLine, taskList[vectIndex]);
	
	return display;
}

string Executor::executePostpone(string inputLine)
{
	UserInterface status;
	string display = EMPTY_STRING;

    string firstWord = extractFirstWord(inputLine);

	if(firstWord == "all")
		display = postponeAllTask(inputLine);
    else
		display = postponeSingleTask(firstWord, inputLine);

	return display;
}

string Executor::postponeAllTask(string inputLine)
{
	Validator checker;
	UserInterface status;
	string display = EMPTY_STRING;
	ostringstream outstr;

	string date_currentlyAt = extractFirstWord(inputLine);
	string date_toPostponeTo = extractFirstWord(inputLine);

	try
	{
		if(!checker.isValidDate(date_toPostponeTo))
			throw Exception("invalidDate");
		if(!checker.isValidPostponeDate(date_currentlyAt, date_toPostponeTo))
			throw Exception("invalidPostpone");
		for(int i = 0; i < taskList.size(); i++)
        {       
            if(taskList[i]->getDate() ==  date_currentlyAt)
			{
				string task = basicTaskInfo(i);
				taskList[i]->setDate(date_toPostponeTo);
				outstr << task << "\n";
			}
        }

		display = outstr.str();
		if(display == EMPTY_STRING)
			throw Exception("noTaskOnCmdDate");
		display = display +  TAB + "have been postponed to " + date_toPostponeTo + "\n";
		status.generateMessage("postponedAll",date_currentlyAt, date_toPostponeTo);
	}
    catch (Exception& e)
    {
		if(!checker.isValidDate(date_currentlyAt) && !checker.isValidDate(date_toPostponeTo))
		{
			string bothDates = date_currentlyAt + " and " + date_toPostponeTo;
			status.generateMessage(e.getErrorType(), bothDates);
		}
		else if(!checker.isValidDate(date_toPostponeTo))
			status.generateMessage(e.getErrorType(), date_toPostponeTo);
		else if(!checker.isValidPostponeDate(date_currentlyAt, date_toPostponeTo))
			status.generateMessage(e.getErrorType(), date_toPostponeTo);
		else
			status.generateMessage(e.getErrorType(), date_currentlyAt, "postponed");
	}

	return display;
}

string Executor::postponeSingleTask(string taskNum_str, string inputLine)
{
	Validator checker;
	UserInterface status;
	int actual_taskNum_int;
	string date_toPostponeTo;
	ostringstream outstr;

	string date_currentlyAt;

	try
	{
		if(!checker.isInt(taskNum_str)) 
			throw Exception("notInt");
		int taskNum_int = makeInt(taskNum_str);
		actual_taskNum_int = taskNum_int - 1;
		if(!checker.isValidTaskNumber(actual_taskNum_int, taskList))
			throw Exception("invalidTaskNum");
		date_toPostponeTo = extractFirstWord(inputLine);
		if(!checker.isValidDate(date_toPostponeTo))
			throw Exception("invalidDate");
		date_currentlyAt = taskList[actual_taskNum_int]->getDate();
		if(!checker.isValidPostponeDate(date_currentlyAt, date_toPostponeTo))
			throw Exception("invalidPostpone");
		string task = basicTaskInfo(actual_taskNum_int);
		taskList[actual_taskNum_int]->setDate(date_toPostponeTo);
		outstr << task << "\n" << TAB << "has been postponed to " << date_toPostponeTo << "\n";
		status.generateMessage("postponedSingle",taskList[actual_taskNum_int]->getName(), date_toPostponeTo);
		display = outstr.str();
	}
	catch (Exception& e)
	{
		if(!checker.isInt(taskNum_str) || !checker.isValidTaskNumber(actual_taskNum_int, taskList))
			status.generateMessage(e.getErrorType(), taskNum_str);
		else if(!checker.isValidDate(date_toPostponeTo))
			status.generateMessage(e.getErrorType(), date_toPostponeTo);
		else //(!checker.isValidPostponeDate(date_currentlyAt, date_toPostponeTo))
			status.generateMessage(e.getErrorType(), date_toPostponeTo);
	}

	return display;
}

string Executor::executeUndo()
{
	Storage store;
	UserInterface status;
	string result;

 	string commandToUndo = store.undidCommand();
	
	try
	{
	if(commandToUndo == INITIAL)
		throw Exception("invalidUndo");
	taskList = store.undo();
	result = commandToUndo + " is undid!";
	}
	catch (Exception& e)
	{
		status.generateMessage(e.getErrorType(), "");
	}

	return result;
}

string Executor::executeHelp()
{
    UserInterface status;
    ostringstream outstr;
    string display = EMPTY_STRING;

    display = status.generateHelpList();

    return display;
}

string Executor::executeExit()
{
    Storage store;
	store.clearLog();
    string display = "Exiting...";

    return display;
}

//For UserInterface to retrieve data from taskList in Executor
bool Executor::isRetrievedTaskList(string date, vector<int> &fileIndex, vector<string> &subject, vector<bool> &done)
{
	Validator validate;
	bool found = false;
	int j = 0;

	date = validate.makeProperDayFormat(date);
	date = validate.makeProperMonthFormat(date);
	
	for(int i = 0; i < taskList.size(); i++)
	{
		if(date == taskList[i]->getDate())
		{
			fileIndex.push_back(i+1);
			subject.push_back(taskList[i]->getName());
			done.push_back(taskList[i]->getDone());
			j++;
			found = true;
		}
	}
	
	return found;
}

bool Executor::isRetrievedFloatingTasks(vector<int> &fileIndex, vector<string> &subject)
{
	bool found = false;
	bool isTaskIncomplete;
	int j = 0;
	
	for(int i = 0; i < taskList.size(); i++)
	{
		isTaskIncomplete = (taskList[i]->getDone() == false);
		if(taskList[i]->getDate() == "" && isTaskIncomplete)
		{
			fileIndex.push_back(i+1);
			subject.push_back(taskList[i]->getName());
			j++;
			found = true;
		}
	}
	
	return found;
}

bool Executor::isRetrievedPendingTasks(vector<int> &fileIndex, vector<string> &subject)
{
	Validator validate;
	bool found = false;
	int j = 0;
	
	for(int i = 0; i < taskList.size(); i++)
	{
		string taskDate = taskList[i]->getDate();
		bool isTaskIncomplete = (taskList[i]->getDone() == false);
		if(!taskDate.empty() && isTaskIncomplete && validate.isNotPastDeadLine(taskDate))
		{
			fileIndex.push_back(i+1);
			subject.push_back(taskList[i]->getName());
			j++;
			found = true;
		}
	}
	
	return found;
}